// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Copyright 2022 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.

#include "clay/flow/layers/backdrop_filter_layer.h"

#include <utility>

#include "skity/geometry/rect.hpp"

namespace clay {

BackdropFilterLayer::BackdropFilterLayer(
    std::shared_ptr<const clay::ImageFilter> filter, clay::BlendMode blend_mode)
    : filter_(std::move(filter)), blend_mode_(blend_mode) {}

void BackdropFilterLayer::Diff(DiffContext* context, const Layer* old_layer) {
  DiffContext::AutoSubtreeRestore subtree(context);
  auto* prev = static_cast<const BackdropFilterLayer*>(old_layer);
  if (!context->IsSubtreeDirty()) {
    FML_DCHECK(prev);
    if (NotEquals(filter_, prev->filter_)) {
      context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(old_layer));
    }
  }

  // Backdrop filter paints everywhere in cull rect
  skity::Rect paint_bounds = context->GetCullRect();
  context->AddLayerBounds(paint_bounds);

  if (filter_) {
    context->GetTransform().MapRect(&paint_bounds, paint_bounds);
    auto filter_target_bounds = paint_bounds;
    filter_target_bounds.RoundOut();
    skity::Rect filter_input_bounds;  // in screen coordinates
    filter_->get_input_device_bounds(
        filter_target_bounds, context->GetTransform(), filter_input_bounds);
    context->AddReadbackRegion(filter_input_bounds);
  }

  DiffChildren(context, prev);

  context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion());
}

void BackdropFilterLayer::Preroll(PrerollContext* context) {
  Layer::AutoPrerollSaveLayerState save =
      Layer::AutoPrerollSaveLayerState::Create(context, true, !!filter_);
  if (context->compositor_state != nullptr) {
    context->compositor_state->PushFilterToVisitedPlatformViews(
        filter_, context->state_stack.local_cull_rect());
  }
  skity::Rect child_paint_bounds = skity::Rect::MakeEmpty();
  PrerollChildren(context, &child_paint_bounds);
  child_paint_bounds.Join(context->state_stack.local_cull_rect());
  set_paint_bounds(child_paint_bounds);
  context->renderable_state_flags = kSaveLayerRenderFlags;
}

void BackdropFilterLayer::Paint(PaintContext& context) const {
  FML_DCHECK(needs_painting(context));

  auto mutator = context.state_stack.save();
  mutator.applyBackdropFilter(paint_bounds(), filter_, blend_mode_);

  PaintChildren(context);
}

}  // namespace clay
